using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OneAuthCenter.Application.DTOs.OAuth;
using OneAuthCenter.Application.Services;

namespace OneAuthCenter.API.Controllers;

/// <summary>
/// OAuth 2.0 控制器
/// </summary>
[ApiController]
[Route("oauth")]
public class OAuthController : ControllerBase
{
    private readonly IOAuthService _oauthService;
    private readonly ILogger<OAuthController> _logger;

    public OAuthController(IOAuthService oauthService, ILogger<OAuthController> logger)
    {
        _oauthService = oauthService;
        _logger = logger;
    }

    /// <summary>
    /// OAuth 2.0 授权端点
    /// </summary>
    [HttpGet("authorize")]
    [Authorize]
    public async Task<IActionResult> Authorize([FromQuery] AuthorizeRequest request)
    {
        try
        {
            // 验证授权请求
            var isValid = await _oauthService.ValidateAuthorizeRequestAsync(request);
            if (!isValid)
            {
                return BadRequest(new { error = "invalid_request", error_description = "授权请求无效" });
            }

            // 获取用户 ID
            var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
            if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var userId))
            {
                return Unauthorized(new { error = "unauthorized", error_description = "无效的用户身份" });
            }

            // 检查用户是否已经同意
            var consentedScopes = await _oauthService.GetConsentedScopesAsync(userId, request.ClientId);
            var requestedScopes = request.Scope?.Split(' ', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();

            // 如果已有同意记录且包含所有请求的作用域，直接生成授权码
            if (consentedScopes != null && requestedScopes.All(s => consentedScopes.Contains(s)))
            {
                var redirectUri = await _oauthService.AuthorizeAsync(request, userId);
                return Redirect(redirectUri);
            }

            // 否则返回需要用户同意的信息
            return Ok(new
            {
                requiresConsent = true,
                clientId = request.ClientId,
                requestedScopes = requestedScopes,
                redirectUri = request.RedirectUri,
                state = request.State
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "授权时发生错误");
            return BadRequest(new { error = "server_error", error_description = "授权失败" });
        }
    }

    /// <summary>
    /// 用户同意授权
    /// </summary>
    [HttpPost("consent")]
    [Authorize]
    public async Task<IActionResult> Consent([FromBody] ConsentRequest request)
    {
        try
        {
            var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
            if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var userId))
            {
                return Unauthorized(new { error = "unauthorized", error_description = "无效的用户身份" });
            }

            await _oauthService.SaveConsentAsync(request, userId);

            // 构建授权请求并生成授权码
            var authorizeRequest = new AuthorizeRequest
            {
                ClientId = request.ClientId,
                ResponseType = "code",
                RedirectUri = request.ReturnUrl ?? string.Empty,
                Scope = string.Join(" ", request.GrantedScopes)
            };

            var redirectUri = await _oauthService.AuthorizeAsync(authorizeRequest, userId);
            return Ok(new { redirectUri });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "保存同意记录时发生错误");
            return BadRequest(new { error = "server_error", error_description = "同意失败" });
        }
    }

    /// <summary>
    /// OAuth 2.0 Token 端点
    /// </summary>
    [HttpPost("token")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> Token([FromForm] TokenRequest request)
    {
        try
        {
            var response = await _oauthService.TokenAsync(request);
            return Ok(response);
        }
        catch (UnauthorizedAccessException ex)
        {
            _logger.LogWarning(ex, "Token 请求失败: {GrantType}", request.GrantType);
            return Unauthorized(new { error = "invalid_grant", error_description = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            _logger.LogWarning(ex, "Token 请求无效: {GrantType}", request.GrantType);
            return BadRequest(new { error = "invalid_request", error_description = ex.Message });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Token 请求时发生错误");
            return StatusCode(500, new { error = "server_error", error_description = "Token 请求失败" });
        }
    }

    /// <summary>
    /// OpenID Connect 发现端点
    /// </summary>
    [HttpGet(".well-known/openid-configuration")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public IActionResult Discovery()
    {
        var baseUrl = $"{Request.Scheme}://{Request.Host}{Request.PathBase}";
        
        return Ok(new
        {
            issuer = baseUrl,
            authorization_endpoint = $"{baseUrl}/oauth/authorize",
            token_endpoint = $"{baseUrl}/oauth/token",
            userinfo_endpoint = $"{baseUrl}/api/user/userinfo",
            jwks_uri = $"{baseUrl}/.well-known/jwks.json",
            end_session_endpoint = $"{baseUrl}/oauth/endsession",
            revocation_endpoint = $"{baseUrl}/oauth/revocation",
            introspection_endpoint = $"{baseUrl}/oauth/introspect",
            scopes_supported = new[] { "openid", "profile", "email", "phone", "address" },
            response_types_supported = new[] { "code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token" },
            grant_types_supported = new[] { "authorization_code", "client_credentials", "password", "refresh_token" },
            subject_types_supported = new[] { "public" },
            id_token_signing_alg_values_supported = new[] { "RS256" },
            token_endpoint_auth_methods_supported = new[] { "client_secret_post", "client_secret_basic" },
            claims_supported = new[] { "sub", "name", "email", "email_verified", "given_name", "family_name", "phone_number" },
            code_challenge_methods_supported = new[] { "plain", "S256" }
        });
    }

    /// <summary>
    /// JWKS (JSON Web Key Set) 端点 - 发布公钥
    /// </summary>
    /// <remarks>
    /// IdentityServer4 标准端点：/.well-known/jwks.json
    /// 符合 RFC 7517 - JSON Web Key (JWK) 规范
    /// 
    /// 用途：
    /// - 让客户端和微服务下载公钥来验证 JWT Token
    /// - 支持密钥轮换（可以发布多个密钥）
    /// - 微服务架构中，其他服务可以独立验证 Token
    /// 
    /// 使用场景：
    /// 1. 客户端应用验证 ID Token
    /// 2. 微服务验证 Access Token
    /// 3. API 网关验证 Token
    /// 4. 第三方应用验证 Token
    /// </remarks>
    [HttpGet("/.well-known/jwks.json")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public IActionResult Jwks()
    {
        try
        {
            // 从服务中获取签名凭证服务
            var signingService = HttpContext.RequestServices
                .GetRequiredService<OneAuthCenter.Infrastructure.Services.SigningCredentialsService>();
            
            // 获取 RSA 公钥参数
            var publicKey = signingService.GetPublicKeyParameters();
            var keyId = signingService.GetKeyId();
            
            // 将 RSA 参数转换为 Base64Url 格式（符合 JWK 规范）
            var modulus = Base64UrlEncode(publicKey.Modulus!);
            var exponent = Base64UrlEncode(publicKey.Exponent!);
            
            // 构建 JWKS 响应
            var jwks = new
            {
                keys = new[]
                {
                    new
                    {
                        kty = "RSA",                    // 密钥类型
                        use = "sig",                    // 用途：签名
                        kid = keyId,                    // 密钥 ID
                        alg = "RS256",                  // 算法
                        n = modulus,                    // RSA 模数（公钥）
                        e = exponent                    // RSA 指数（公钥）
                    }
                }
            };
            
            _logger.LogDebug("JWKS 端点被访问");
            return Ok(jwks);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "生成 JWKS 响应失败");
            return StatusCode(500, new { error = "server_error", error_description = "无法生成 JWKS" });
        }
    }

    /// <summary>
    /// Token 撤销端点（符合 RFC 7009）
    /// </summary>
    /// <remarks>
    /// IdentityServer4 标准端点：/connect/revocation
    /// 符合 RFC 7009 - OAuth 2.0 Token Revocation
    /// 
    /// 支持撤销：
    /// - Refresh Token（推荐）
    /// - Access Token（通过黑名单机制）
    /// 
    /// 认证方式：
    /// - client_secret_post（在 body 中传递客户端凭证）
    /// - client_secret_basic（HTTP Basic 认证）
    /// </remarks>
    [HttpPost("revocation")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> Revocation([FromForm] string token, [FromForm] string? token_type_hint)
    {
        try
        {
            if (string.IsNullOrEmpty(token))
            {
                return BadRequest(new { error = "invalid_request", error_description = "token 参数缺失" });
            }

            // 根据 token_type_hint 处理不同类型的 Token
            if (token_type_hint == "refresh_token" || token_type_hint == null)
            {
                // 尝试撤销 Refresh Token
                var refreshTokenRepo = HttpContext.RequestServices
                    .GetRequiredService<OneAuthCenter.Domain.Repositories.IRefreshTokenRepository>();
                await refreshTokenRepo.RevokeAsync(token);
            }

            if (token_type_hint == "access_token" || token_type_hint == null)
            {
                // 尝试撤销 Access Token（加入黑名单）
                try
                {
                    var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
                    var jwtToken = handler.ReadJwtToken(token);
                    var jti = jwtToken.Claims.FirstOrDefault(c => c.Type == "jti")?.Value;
                    var userIdClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
                    var exp = jwtToken.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;

                    if (!string.IsNullOrEmpty(jti) && !string.IsNullOrEmpty(userIdClaim) && int.TryParse(userIdClaim, out var userId))
                    {
                        var expiresAt = DateTimeOffset.FromUnixTimeSeconds(long.Parse(exp!)).UtcDateTime;
                        var revokedTokenRepo = HttpContext.RequestServices
                            .GetRequiredService<OneAuthCenter.Domain.Repositories.IRevokedTokenRepository>();
                        
                        var tokenHash = ComputeSha256Hash(token);
                        await revokedTokenRepo.AddAsync(new OneAuthCenter.Domain.Entities.RevokedToken
                        {
                            TokenId = jti,
                            UserId = userId,
                            TokenHash = tokenHash,
                            RevokedAt = DateTime.UtcNow,
                            ExpiresAt = expiresAt,
                            Reason = "通过撤销端点撤销"
                        });
                    }
                }
                catch
                {
                    // 忽略 Access Token 解析错误
                }
            }

            _logger.LogInformation("Token 已撤销");
            
            // RFC 7009 规定：无论 Token 是否存在，都返回 200 OK
            return Ok();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "撤销 Token 时发生错误");
            return StatusCode(500, new { error = "server_error" });
        }
    }

    /// <summary>
    /// 结束会话端点（登出）
    /// </summary>
    /// <remarks>
    /// IdentityServer4 标准端点：/connect/endsession
    /// OpenID Connect Session Management 规范
    /// 
    /// 参数：
    /// - id_token_hint: ID Token（可选，用于识别用户）
    /// - post_logout_redirect_uri: 登出后重定向 URI
    /// - state: 状态参数
    /// </remarks>
    [HttpGet("endsession")]
    [HttpPost("endsession")]
    [ProducesResponseType(StatusCodes.Status302Found)]
    public async Task<IActionResult> EndSession(
        [FromQuery] string? id_token_hint,
        [FromQuery] string? post_logout_redirect_uri,
        [FromQuery] string? state)
    {
        try
        {
            // 如果有 ID Token，解析并撤销用户的所有 Token
            if (!string.IsNullOrEmpty(id_token_hint))
            {
                try
                {
                    var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
                    var jwtToken = handler.ReadJwtToken(id_token_hint);
                    var userIdClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;

                    if (!string.IsNullOrEmpty(userIdClaim) && int.TryParse(userIdClaim, out var userId))
                    {
                        var refreshTokenRepo = HttpContext.RequestServices
                            .GetRequiredService<OneAuthCenter.Domain.Repositories.IRefreshTokenRepository>();
                        await refreshTokenRepo.RevokeAllByUserIdAsync(userId);
                        
                        _logger.LogInformation("用户登出，UserId: {UserId}", userId);
                    }
                }
                catch
                {
                    // 忽略 ID Token 解析错误
                }
            }

            // 重定向到指定的 URI
            if (!string.IsNullOrEmpty(post_logout_redirect_uri))
            {
                var redirectUri = post_logout_redirect_uri;
                if (!string.IsNullOrEmpty(state))
                {
                    redirectUri += $"?state={System.Web.HttpUtility.UrlEncode(state)}";
                }
                return Redirect(redirectUri);
            }

            return Ok(new { message = "已成功登出" });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "结束会话时发生错误");
            return StatusCode(500, new { error = "server_error" });
        }
    }

    /// <summary>
    /// Token 自省端点（符合 RFC 7662）
    /// </summary>
    /// <remarks>
    /// IdentityServer4 标准端点：/connect/introspect
    /// 符合 RFC 7662 - OAuth 2.0 Token Introspection
    /// 
    /// 用途：
    /// - 验证 Token 是否有效
    /// - 获取 Token 的元数据（过期时间、scope、用户信息等）
    /// - 资源服务器验证 Access Token
    /// 
    /// 认证：
    /// - 需要客户端凭证（client_id + client_secret）
    /// - 支持 client_secret_post 和 client_secret_basic
    /// 
    /// 返回：
    /// - active: true/false（Token 是否有效）
    /// - 如果有效，返回 Token 的详细信息
    /// </remarks>
    [HttpPost("introspect")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> Introspect(
        [FromForm] string token,
        [FromForm] string? token_type_hint,
        [FromForm] string? client_id,
        [FromForm] string? client_secret)
    {
        try
        {
            // 1. 验证参数
            if (string.IsNullOrEmpty(token))
            {
                return BadRequest(new { error = "invalid_request", error_description = "token 参数缺失" });
            }

            // 2. 验证客户端凭证（可选，但推荐）
            if (!string.IsNullOrEmpty(client_id) && !string.IsNullOrEmpty(client_secret))
            {
                var clientRepo = HttpContext.RequestServices
                    .GetRequiredService<OneAuthCenter.Domain.Repositories.IClientRepository>();
                
                if (!await clientRepo.ValidateClientAsync(client_id, client_secret))
                {
                    _logger.LogWarning("Token 自省失败：客户端凭证无效，ClientId: {ClientId}", client_id);
                    return Unauthorized(new { error = "invalid_client" });
                }
            }

            // 3. 尝试解析和验证 Token
            try
            {
                var tokenService = HttpContext.RequestServices
                    .GetRequiredService<OneAuthCenter.Application.Services.ITokenService>();
                
                var principal = tokenService.ValidateAccessToken(token);
                
                // 如果 Token 无效
                if (principal == null)
                {
                    return Ok(new { active = false });
                }

                // 4. 检查 Token 是否在黑名单中
                var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
                var jwtToken = handler.ReadJwtToken(token);
                var jti = jwtToken.Claims.FirstOrDefault(c => c.Type == "jti")?.Value;
                
                if (!string.IsNullOrEmpty(jti))
                {
                    var revokedTokenRepo = HttpContext.RequestServices
                        .GetRequiredService<OneAuthCenter.Domain.Repositories.IRevokedTokenRepository>();
                    
                    if (await revokedTokenRepo.IsRevokedAsync(jti))
                    {
                        _logger.LogDebug("Token 已被撤销，Jti: {Jti}", jti);
                        return Ok(new { active = false });
                    }
                }

                // 5. Token 有效，返回详细信息
                var sub = principal.FindFirst("sub")?.Value;
                var clientIdClaim = principal.FindFirst("client_id")?.Value;
                var scopes = principal.FindAll("scope").Select(c => c.Value).ToArray();
                var exp = jwtToken.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;
                var iat = jwtToken.Claims.FirstOrDefault(c => c.Type == "iat")?.Value;
                var iss = jwtToken.Claims.FirstOrDefault(c => c.Type == "iss")?.Value;
                var aud = jwtToken.Claims.FirstOrDefault(c => c.Type == "aud")?.Value;
                
                // 构建响应（符合 RFC 7662）
                var response = new Dictionary<string, object>
                {
                    ["active"] = true,
                    ["token_type"] = "Bearer",
                    ["sub"] = sub ?? "",
                    ["exp"] = exp != null ? long.Parse(exp) : 0,
                    ["iat"] = iat != null ? long.Parse(iat) : 0
                };

                if (!string.IsNullOrEmpty(iss))
                    response["iss"] = iss;
                
                if (!string.IsNullOrEmpty(aud))
                    response["aud"] = aud;
                
                if (!string.IsNullOrEmpty(clientIdClaim))
                    response["client_id"] = clientIdClaim;
                
                if (scopes.Length > 0)
                    response["scope"] = string.Join(" ", scopes);
                
                if (!string.IsNullOrEmpty(jti))
                    response["jti"] = jti;

                // 添加用户信息（如果有）
                var username = principal.FindFirst(System.Security.Claims.ClaimTypes.Name)?.Value;
                if (!string.IsNullOrEmpty(username))
                    response["username"] = username;
                
                var email = principal.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value;
                if (!string.IsNullOrEmpty(email))
                    response["email"] = email;

                _logger.LogDebug("Token 自省成功，Sub: {Sub}", sub);
                return Ok(response);
            }
            catch (Exception ex)
            {
                _logger.LogDebug(ex, "Token 解析失败");
                return Ok(new { active = false });
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Token 自省时发生错误");
            return StatusCode(500, new { error = "server_error" });
        }
    }

    /// <summary>
    /// Base64Url 编码（符合 JWT/JWK 规范）
    /// </summary>
    private static string Base64UrlEncode(byte[] input)
    {
        var base64 = Convert.ToBase64String(input);
        // Base64Url 编码：替换 +/= 字符
        return base64.Replace("+", "-").Replace("/", "_").Replace("=", "");
    }

    /// <summary>
    /// 计算 SHA256 哈希
    /// </summary>
    private static string ComputeSha256Hash(string input)
    {
        using var sha256 = System.Security.Cryptography.SHA256.Create();
        var hashBytes = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input));
        return Convert.ToHexString(hashBytes).ToLower();
    }
}

